#include "audio_sr.h"

my_sr_t my_sr;

void audio_sr_init(void)
{
    // 1.初始化语音识别模型. 参数就是存储模型的分区标签
    srmodel_list_t *models = esp_srmodel_init("model");
    // 2. 音频前端初始化配置  参数1:输入类型(一个麦克风) 参数2:语音识别模型 参数3: 语音识别类型(语音识别)
    afe_config_t *afe_config = afe_config_init("M", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);
    // 补充: 前端优化 TODO
    afe_config->aec_init = false;   // 回升消除
    afe_config->ns_init  = false;   // 噪声抑制
    afe_config->se_init  = false;   // 语音增强

    afe_config->vad_init         = true;         // 语音活动检测
    afe_config->vad_mode         = VAD_MODE_2;   // 数字越小, 越敏感
    afe_config->vad_min_noise_ms = 500;          // 噪声检测阈值

    afe_config->wakenet_init = true;          // 语音唤醒
    afe_config->wakenet_mode = DET_MODE_90;   // 唤醒模式: 值越大越容易被唤醒, 但是容易误唤醒

    // 3. 获取句柄: 操作音频前端
    my_sr.afe_handle = esp_afe_handle_from_config(afe_config);
    // 4. 创建实例: 读写数据
    my_sr.afe_data = my_sr.afe_handle->create_from_config(afe_config);
}

void feed_task(void *);
void fetch_task(void *);

// 回调函数1: 唤醒后的回调  回调函数2:当语音状态发声变化(说话->静音   静音->说话)
void audio_sr_start(RingbufHandle_t ringBuf,
                    void (*wake_cb)(void),
                    void (*vad_change_cb)(vad_state_t))
{
    my_sr.is_running = true;
    my_sr.is_wake    = false;

    my_sr.wake_cb       = wake_cb;
    my_sr.vad_change_cb = vad_change_cb;
    // 启动两个任务:
    // 1. 喂数据: 从es8311读取音频数据, 给sr模型
    // 2. 从sr模型读取数据, 然后存入到环形缓冲区中

    my_sr.ringBuf = ringBuf;

    // xTaskCreate(feed_task, "feed_task", 4 * 1024, NULL, 5, NULL);
    // xTaskCreate(fetch_task, "read_task", 4 * 1024, NULL, 5, NULL);
    xTaskCreateWithCaps(feed_task, "feed_task", 4 * 1024, NULL, 5, NULL, MALLOC_CAP_SPIRAM);
    xTaskCreateWithCaps(fetch_task, "read_task", 4 * 1024, NULL, 5, NULL, MALLOC_CAP_SPIRAM);
}

void feed_task(void *args)
{

    // 将来从es8311获取的音频片段的大小
    int feed_chunksize = my_sr.afe_handle->get_feed_chunksize(my_sr.afe_data);
    // 获取通道数
    int feed_nch = my_sr.afe_handle->get_feed_channel_num(my_sr.afe_data);

    int size = feed_chunksize * feed_nch * sizeof(int16_t);
    // 存储音频片段的缓冲区
    int16_t *feed_buff = (int16_t *)malloc(size);

    while(my_sr.is_running)
    {
        // 从es8311获取音频片段
        bsp_sound_read(feed_buff, size);
        // 把获取到的数据喂给sr模型
        my_sr.afe_handle->feed(my_sr.afe_data, feed_buff);
    }

    free(feed_buff);
    vTaskDelete(NULL);
}
void fetch_task(void *args)
{

    while(my_sr.is_running)
    {
        // 获取语音识别后的结果
        afe_fetch_result_t *result = my_sr.afe_handle->fetch(my_sr.afe_data);

        // 具体的识别后的音频数据
        int16_t *processed_audio = result->data;
        // 当前语音状态: 静音or说话
        vad_state_t vad_state = result->vad_state;
        // 唤醒状态: 当前语音是否在唤醒
        wakenet_state_t wakeup_state = result->wakeup_state;

        // 检测到唤醒词
        if(wakeup_state == WAKENET_DETECTED)
        {
            my_sr.is_wake        = true;
            my_sr.last_vad_state = VAD_SILENCE;   // 唤醒之后, vad设置为静音
            if(my_sr.wake_cb)
            {
                my_sr.wake_cb();
            }
        }

        // 处理 vad
        if(my_sr.is_wake)
        {
            if(vad_state != my_sr.last_vad_state)
            {
                // 更新上一次的语音状态
                my_sr.last_vad_state = vad_state;

                if(my_sr.vad_change_cb)
                {
                    my_sr.vad_change_cb(vad_state);
                }
            }
        }

        // 处理说话的片段: 存入到环形缓冲区
        if(my_sr.is_wake && vad_state == VAD_SPEECH)
        {

            // if vad cache is exists, please attach the cache to the front of processed_audio to avoid data loss
            if(result->vad_cache_size > 0)
            {
                int16_t *vad_cache = result->vad_cache;
                if(my_sr.ringBuf)
                {
                    xRingbufferSend(my_sr.ringBuf, vad_cache, result->vad_cache_size, 0);
                }
            }

            // 向环形缓冲区存入数据
            if(my_sr.ringBuf)
            {
                xRingbufferSend(my_sr.ringBuf, processed_audio, result->data_size, 0);
            }
        }
    }

    vTaskDelete(NULL);
}
